#include <linux/config.h>
#include <linux/init.h>
#include <linux/suspend.h>
#include <linux/errno.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/crc32.h>
#include <linux/ioport.h>
#include <linux/delay.h>

#include <asm/string.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/arch/sep4020_hal.h>
#include <linux/spinlock.h>

spinlock_t sep4020_sleep_lock;

static int sep4020_pm_debug_init(void)
{
	return 0;
}

static void sep4020_pm_configure_fiq(void)
{
    *(volatile unsigned long*)INTC_FIMR_V = 0x0;
    *(volatile unsigned long*)INTC_FIER_V  = 0x8;
}

/* helper functions to save and restore register state, we donot need this now */


void sep4020_sleep(void)
{
	__asm__(
	"mov r0, #0xe0000004;"
 	" orr r0, r0, #0x1000;"
 	"orr r0, r0, #0x10;"
	" mov  r1, #0x2;"
	" mov  r2, #0x0;"
	" mov  r3, #0x0;"
	" mov  r4, #0x0;"
	" mov r5, #0x0;"
	" stmia  r0!, {r1,r2,r3,r4,r5};"
	"nop;"
	"nop;"
	"nop;"
	"nop;"
	"nop;"
	"nop;"
	"nop;"
	"nop;"
	"nop;"	
	);
}


static int set_all_io_in(void)
{
	SET_PORT_MASK(GPIO_PORTG_SEL_V, 0x20);			//PG5 WIFI
	SET_PORT_MASK(GPIO_PORTG_DIR_V, 0x20);


	SET_PORT_MASK(GPIO_PORTD_SEL_V, 0x8);      //pd3 闲置
	SET_PORT_MASK(GPIO_PORTD_DIR_V, 0x8);
	
	SET_PORT_MASK(GPIO_PORTC_SEL_V, 0x4);   //pc2  闲置	
	SET_PORT_MASK(GPIO_PORTC_DIR_V, 0x4);

}

static int sep4020_pm_enter(suspend_state_t state)
{
	printk("in the sep4020_pm_enter\n");


	/* ensure the debug is initialised (if enabled) */

	sep4020_pm_debug_init();

	if (state != PM_SUSPEND_MEM) 
	{
		printk(KERN_ERR "error: only PM_SUSPEND_MEM supported\n");
		return -EINVAL;
	}

	/* donot need checking if we have anything to wake-up with...sep4020 will always 
		wakeup if there is a wakeupsignal, the signal will not care the int controller
	*/

	/* we donot save all core registers , because sleep supported by sep4020 is ram based ,
	it donot cut the power off, but in other soc we need to do this  */


	/* set the fiq configuration for wake ,although the system wakeup donot need to pass the intc.
		but you can still use the fiq asm handler to clear some bits, if you are sure of this ,you should configure the fiq of intc first,
	 */
		//sep4020_pm_configure_fiq();
	

	//this is for logic test as a level change
	/*
	*(volatile unsigned long*)GPIO_PORTG_SEL_V |= 0x1000;
	*(volatile unsigned long*)GPIO_PORTG_DATA_V &= ~0x1000;
	*/
	
set_all_io_in();
	spin_lock_irq(&sep4020_sleep_lock);

	__asm__(
	"stmfd sp!, {r0, r1, r2,r3, r4,r5};"
	"mov r0, #0xe0000004;"
 	" orr r0, r0, #0x1000;"
 	"orr r0, r0, #0x10;"
	" mov  r1, #0x2;"
	" mov  r2, #0x0;"
	" mov  r3, #0x0;"
	" mov  r4, #0x0;"
	" mov r5, #0x0;"
	" stmia  r0!, {r1,r2,r3,r4,r5};"
	"nop;"
	"nop;"
	"nop;"
	"nop;"
	"nop;"
	"nop;"
	"nop;"
	"nop;"
	"nop;"	
	"ldmfd sp!, {r0,r1, r2, r3, r4,r5};"
	);

	spin_unlock_irq(&sep4020_sleep_lock);
/*******************************************the following is wake up code******************/
	
	/*this is asm wake up code*/
	/*	
	__asm__(
	"mov	r8, #0xe0000004\n"
	"orr	r8, r8, #0x1000\n"
	"orr	r8, r8, #0x10\n"
	"mov	r9, #0x1\n"
	"str	r9, [ r8 ]\n"	
	);
	*/

	*(volatile unsigned long*)0xe0001014 = 0x1;

	//here we need sometime to stable the uart,the time need to be considerable
	mdelay(100);
	/*	beep for debug*/
/*
   *(volatile unsigned long*)GPIO_PORTA_SEL_V |= (1 << 6); //作为通用用途
	*(volatile unsigned long*)GPIO_PORTA_DIR_V &= ~(1 << 6);//输出


	*(volatile unsigned long*)GPIO_PORTA_DATA_V |= (1 << 6);
	for(i = 0; i < 1000000; i++);
	*(volatile unsigned long*)GPIO_PORTA_DATA_V &= ~(1 << 6);
	for(i = 0; i < 1000000; i++);
*/

	return 0;
}

static int sep4020_pm_prepare(suspend_state_t state)
{
	printk("in the sep4020_pm_prepare\n");
	return 0;
}

/*
 * Called after devices are re-setup, but before processes are thawed.
 */
static int sep4020_pm_finish(suspend_state_t state)
{
	printk("in the sep4020_pm_finish\n");
	return 0;
}

/*
 * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
 */
static struct pm_ops sep4020_pm_ops = {
	.pm_disk_mode	= PM_DISK_FIRMWARE,
	.prepare		= sep4020_pm_prepare,
	.enter		= sep4020_pm_enter,
	.finish		= sep4020_pm_finish,
};

int __init sep4020_pm_init(void)
{
	pm_set_ops(&sep4020_pm_ops);
	return 0;
}

arch_initcall(sep4020_pm_init);

